<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
        <title>模块加载内部运作情况</title>
    </head>
    <body>
        <h3>模块加载内部运作情况</h3>
        <p>下面揭载我的加载器的运作机理，姑且从一个没有依赖的模块着手，比如</p>
        <pre class="brush:js;gutter:false;toolbar:false;">
            $.require("$lang_fix", function(){
                console.log("xxxxxxxxxxxxxx")
            })
        </pre>
        <p>进入$.require</p>
        <pre class="brush:js;gutter:false;toolbar:false;">
 String(list).replace( $.rword, function(el){

  }）

el  = "$lang_fix"
        </pre>
        <p>进入Module._resolveFilename</p>
        <pre class="brush:js;gutter:false;toolbar:false;">

url = $.core.base + el + ".js"
        </pre>

        <p>初次加载，肯定没有在modules中注册， modules[url]为undefined</p>

        <p>于是进入 loadJS( url, id );</p>
        <ul>
            <li>创建一个iframe，</li>
            <li>在第一个script节点 用nick, Ns, nick分别保存url, $, innerDefine</li>
            <li>在第二个script节点 用url去加载目标节点</li>
        </ul>

        <p>第二个节点视情况不同分别绑定onreadystatechange, onload, onerror</p>

        <p>值得注意的是我们在注册模块时state还是为undefined，它会在innerDefine中修改state。</p>
        然后我们通过iframe中的script加载模块,而模块一般是这样的格式
        <pre class="brush:js;gutter:false;toolbar:false;">
define("lang_fix", function( ){
  //==========略============
})
        </pre>
        <p>这里的define实质上是innerDefine</p>

        <p>innerDefine里面做了几个很重要的事情（我们现在只需看第一，第二）</p>
        <ol>
            <li>第一个事情把lang_fix这第一个参数换掉,换成nick, nick就是加载它的那个script的src。</li>
            <li>第二个事情是将它对应的模块的状态改为1，也是modules[url].state = 1。</li>

            <li>第三个事情是$, exports, require, module等对象强塞进模块工厂</li>
            <li>第四个事情是转交真正的$.define去处理</li>
        </ol>
        <p>在第二事情中，我们将模块的状态修改了，于是节点执行onreadystatechange/onload时</p>
        <pre class="brush:js;gutter:false;toolbar:false;">
if(/loaded|complete|undefined/i.test(this.readyState) }{
    Ns._checkDeps();
     Ns._checkFail(self.document, nick);
}
        </pre>
        <p>Ns._checkFail发挥效力</p>
        <pre class="brush:js;gutter:false;toolbar:false;">
_checkFail : function(  doc, id, error ){
    doc && (doc.ok = 1);
    if( error || !modules[ id ].state ){
         this.log("Failed to load [[ "+id+" ]]"+modules[ id ].state);
    }
},
        </pre>
        <p>如果是死链，那无法调用define函数，也就无法调用innerDefine，状态为undefine</p>
        <p><strong>!modules[ id ].state == true</strong>，于是打印错误日志，当然以后我们讨论一下，是不是该throw</p>

        <p>如果在旧式opera,它是会进入onreadystatechange, onload, onerror这任一回调时</p>
        <p>在_checkFail中，我们会修复doc.ok = 1，那么在iframe中onload中我们检测doc.ok不等于1时，</p>
        <p>就在_checkFail传入第三个参数true，让它打印错误日志</p>

        <p>如果在FF，chrome, IE9，它们就会进入onerror回调，那里的调用代码中</p>
        <pre class="brush:js;gutter:false;toolbar:false;">

Ns._checkFail(self.document, nick, true)
        </pre>
        <p>因此也顺利检测到死链。</p>

        <p>反正只要加载失败,我们就立即把对应iframe移出DOM树!</p>

        <p>好了，如果成功加载，我们就通过innerDefine到达$.define，并也把对应iframe移出DOM树</p>

        <p>$.define干了如下几个事情</p>
        <ol>
            <li>第一个事情，检测第二个参数是否为布尔，是说明其是补丁模块，如果布尔值为true，说明这个补丁模块对这个浏览器是没有用的，直接return，不执行它的模块工厂了。如果为false，比如IE6，补丁模块对它总是有用，先去掉此参数，继续往下执行。</li>

            <li>第二个事情，检测参数个数，如果只有两个，说明只有模块名与模块的回调（亦有可能不是回调），那么我们插入一个空数组作为依赖列表</li>

            <li>第三个就是检测第三个参数是否为函数，不是函数，比如说是个对象，我们要将它塞入一个函数</li>
        </ol>
        <p>比如</p>
        <pre class="brush:js;gutter:false;toolbar:false;">

define({
  aaa:2
})
        </pre>
        <p>经上面几次转换，依次变为</p>
        <pre class="brush:js;gutter:false;toolbar:false;">

define("http://xxxxxxx/aa.js",{
  aaa:2
});
//---->
define("http://xxxxxxx/aa.js" ,[],{
  aaa:2
});

//---->
define("http://xxxxxxx/aa.js" ,[], function(){
 return {
    aaa:2
  }
});

//---->
$.rquire([], function(){
 return {
    aaa:2
  }
}), "http://xxxxxxx/aa.js")
        </pre>
        <p>最后我们将参数的顺序重排一下，再次调用$.require</p>
        <pre class="brush:js;gutter:false;toolbar:false;">
 //0,1,2 --> 1,2,0
 this.require( args[1], args[2], parent );
        </pre>
        <p>由于lang_fix是没有依赖的，因此dn === cn 相当于 0 == 0，执行install( id, args, factory );</p>

        <p>install简而言之是将模块工厂执行，将state改为2。</p>

        <p>我们还需要注意一下，我们每次调用$.require或加载一个脚本时都执行_checkDeps方法。</p>

        <p>当lang_fix的状态改为2后</p>
        <pre class="brush:js;gutter:false;toolbar:false;">


 $.require("$lang_fix", function(){
      console.log("xxxxxxxxxxxxxx")
 })
        </pre>
        <p>这个回调也将执行！</p>
        <p>控制台打印xxxxxxxxxxxxxxxx</p>

    </body>
</html>
